/*
The hdd.c file implements the low-level TTY (console) driver
*/

#include <globals.h>
#include <klib.h>
#include <interrupts.h>
#include <panic.h>
#include <tty.h>
#include <kbd.h>


//Private Includes


/* Data structures */

#define K_NORM_ATT      0x07
//#define K_VIDEO_MEM     0x7FFB8000 // this is because kernel is linked at 2GiB... needs to be changed
//#define K_VIDEO_MEM     0xB8000 
#define K_VIDEO_MEM     0xF0000000 


//crtc_adr = 0x3D4; /* 0x3B4 for monochrome */
#define CRTC_ADDR       0x03D4


// declare system tty
tty_t *system_tty=NULL;

// tty of the current process
tty_t *current_tty=NULL;

/* Private functions prototypes */

inline word_t pack_char(int ch);

void scroll_up(tty_t *tty);
int getPos(tty_t *tty);
void incCol(tty_t *tty);
void incRow(tty_t *tty);
void move_cursor(tty_t *tty);
int detect_video_type(void);

/* Functions implementation */

/* Private functions */

inline word_t pack_char(int ch)
{
  return (word_t)(((word_t)(system_tty->color)<<8) | (((word_t)ch)&0x0FF));
}

/* Public functions */

void tty_initialize(void)
  {
    // allocate system tty
    system_tty=(tty_t *)malloc(sizeof(tty_t));

    system_tty->video_memory=(word_t *)K_VIDEO_MEM;
    system_tty->size=2000; // 80x25 (words)
    // current row and col
    system_tty->row=0;
    system_tty->col=0;
    // current color
    system_tty->color=K_NORM_ATT;
    // max row and col
    system_tty->max_row=25;
    system_tty->max_col=80;
    // it's a BIOS-based tty
    system_tty->flags=TTY_FLG_BIOS;
    // echo on
    system_tty->echo=TTY_ECHO_ON;
    // initialize lock
    lock_initialize(&system_tty->mutex);

    current_tty=system_tty;
    
    //assign_IRQ(1,(dword_t)&hdd_interrupt);
  }

/*
    int hwi;

    hwi=lock_acquire_cli(&mutex);

    ret=buffer_dequeue((int *)code);

    lock_release_sti(&mutex,hwi);

*/


void tty_clear(tty_t *tty)
  {
    int i;
    word_t *p;
    word_t wrr;
    int hwi;

    hwi=lock_acquire_cli(&tty->mutex);

    // blank char
    wrr=pack_char(' ');
    
    // point to tty video memory
    p=tty->video_memory;

    for (i=0;i<tty->size;i++)
      *p++=wrr;    

    // reset tty status
    tty->row=0;
    tty->col=0;
    move_cursor(tty);

    lock_release_sti(&tty->mutex,hwi);


    /*
    int i;
    word_t *p=(word_t *)K_VIDEO_MEM;
    word_t wrr=K_NORM_ATT;

    wrr<<=8;
    wrr|=32;
    
    for (i=0;i<2000;i++)
      *p++=wrr;    

    row=col=0;
    move_cursor(row,col);
    */
  }

int tty_write_char(tty_t *tty,int c)
  {
    word_t ch;
    word_t *p;
    int hwi;

    if (current_tty==NULL)
      return 1;

    hwi=lock_acquire_cli(&tty->mutex);

    ch=pack_char((word_t)c);

    // point to tty video memory
    p=tty->video_memory;
    
    if (c=='\n')
      {
	tty->col=0;
	incRow(tty);
	//move_cursor(tty->row,tty->col);
	move_cursor(tty);
      }
    else
      {
	//*(p+getPos(tty->row,tty->col))=ch;    
	*(p+getPos(tty))=ch;
	incCol(tty);

	move_cursor(tty);
      }

    lock_release_sti(&tty->mutex,hwi);

    return c;    

    /*
    word_t ch=K_NORM_ATT<<8 | (char)c;
    word_t *p=(word_t *)K_VIDEO_MEM;

    if (c=='\n')
      {
	col=0;
	incRow();
	move_cursor(row,col);

	return c;
      }
 
    *(p+getPos(row,col))=ch;
    incCol();

    move_cursor(row,col);
        
    return c;    
    */
  }

int tty_read_char(tty_t *tty,int *c)
  {
    *c=0;
    return 0;
  }

/*
void scroll_up(tty_t *tty)
  {
    int i;
    word_t *p1,*p2;
    word_t ch;
    int top;

    // blank
    ch=pack_char(' ');

    p1=tty->video_memory;
    p2=p1+tty->max_col; //next row

    // all memory minus one row
    top=tty->size-tty->max_col;

    // copy (scroll up)
    for (i=0;i<top;i++)
      *p1++=*p2++;
    
    // blank last line
    for (i=0;i<tty->max_col;i++)
      *p1++=ch;
  }
*/

// Faster version: use dword_t instead of word_t to copy memory
void scroll_up(tty_t *tty)
  {
    int i;
    dword_t *p1,*p2;
    dword_t ch;
    word_t ch1;
    int top;

    // blank
    ch1=pack_char(' ');
    ch=(dword_t)ch1<<16 | ch1;

    p1=(dword_t *)(tty->video_memory);
    p2=p1+(tty->max_col>>1); //next row

    // all memory minus one row
    top=(tty->size-tty->max_col)>>1;

    // copy (scroll up)
    for (i=0;i<top;i++)
      *p1++=*p2++;
    
    // blank last line
    top=tty->max_col>>1;
    for (i=0;i<top;i++)
      *p1++=ch;
  }


int getPos(tty_t *tty)
  {
    return tty->row*tty->max_col+tty->col;
  }

void incRow(tty_t *tty)
  {
    tty->row++;

    if (tty->row>=tty->max_row)
      {
	scroll_up(tty);
	tty->row=tty->max_row-1;
	tty->col=0;
      }
  }

void incCol(tty_t *tty)
  {
    tty->col++;

    if (tty->col>=tty->max_col)
      {
	tty->col=0;
	incRow(tty);	  
      }
  }


void delay(void)
{
    int i,h;


    for (i=0;i<10000;i++)
      {
	h=i*2;
	h-=i;
      }
    i=h+1;
    if (i==2)
      h=3;
    else
      h=5;
    i=h;
  }  

void move_cursor(tty_t *tty)
  {
    int cpos=getPos(tty);
    
    // select "cursor position MSB" in vga index register
    outb(CRTC_ADDR,0x0E); 
    // write cursor position MSB to vga data register
    outb(CRTC_ADDR+1,((cpos>>8)&0xFF));
    // select "cursor position LSB" in vga index register
    outb(CRTC_ADDR,0x0F); 
    // write cursor position LSB to vga data register
    outb(CRTC_ADDR+1,(cpos&0xFF));    
  }

     

   
/* video card mono/color detection by Dark Fiber
 * returns 0=mono, 1=color
 */
int detect_video_type(void)
  {
    int rc;    
    char c=*((word_t *)0x410)&0x30;
    //char c=(*(USHORT*)0x410&0x30;
	    
    /* C can be 0x00 or 0x20 for color, 0x30 for mono */
    if(c==0x30)
      rc=0;	// mono
    else
      rc=1;	// color
    
    return rc;
  }
  

	     

